unit FMain;

interface

uses
  Winapi.OpenGL,
  Winapi.Windows,
  Winapi.Messages,
  System.Math,
  System.SysUtils,
  System.UITypes,
  System.Variants,
  System.Classes,
  Vcl.Graphics,
  Vcl.Controls,
  Vcl.Forms,
  Vcl.Dialogs,
  Vcl.StdCtrls,
  Vcl.ExtCtrls,
  Vcl.Menus,

  GLS.PersistentClasses,
  GLS.VectorTypes,
  GLS.Scene,
  GLS.State,
  GLS.VectorTypesExt,
  GLS.SceneViewer,
  GLS.Objects,
  GLS.VectorFileObjects,
  GLS.VectorGeometry,
  GLS.Coordinates,
  
  GLS.BaseClasses,
  GLS.IsosurFace,
  GLS.Spline,
  Vcl.Mask;

type
  TFrmMain = class(TForm)
    GLSceneViewer: TGLSceneViewer;
    GLScene: TGLScene;
    OpenDialog: TOpenDialog;
    Camera: TGLCamera;
    dcBox: TGLDummyCube;
    PUSerInterface: TPanel;
    ffCube: TGLFreeForm;
    Label1: TLabel;
    Label2: TLabel;
    lblVertices: TLabel;
    lblTriangles: TLabel;
    LEXDim: TLabeledEdit;
    LEYDim: TLabeledEdit;
    LEZDim: TLabeledEdit;
    LEIsoVal: TLabeledEdit;
    rbgAlgorithm: TRadioGroup;
    MainMenu: TMainMenu;
    File1: TMenuItem;
    miFileOpen: TMenuItem;
    miFileExit: TMenuItem;
    N3: TMenuItem;
    Light: TGLLightSource;
    rbgSurface: TRadioGroup;
    procedure FormCreate(Sender: TObject);
    procedure FormMouseWheel(Sender: TObject; Shift: TShiftState;
      WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
    procedure GLSceneViewerMouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure GLSceneViewerMouseMove(Sender: TObject; Shift: TShiftState;
      X, Y: Integer);
    procedure miFileOpenClick(Sender: TObject);
    procedure miFileExitClick(Sender: TObject);
    procedure rbgAlgorithmClick(Sender: TObject);
    procedure rbgSurfaceClick(Sender: TObject);
  private
    ExtractedVertices: TVertexArray; // array of TVertex
    ExtractedTriangles: TIntegerArray; // array of Integer
    Dimensions: array ['x' .. 'z'] of Word;

    ExtendedData: TArray3DExt; // array of array of array of Single
    mdx, mdy: Integer;

    // Load Data from file
    function LoadCharData(AFileName: String; out ADataout: TArray3DExt;
      var Dims: array of word): Integer;
  public
    { Public-Deklarationen }
  end;

var
  FrmMain: TFrmMain;

implementation

{$R *.dfm}

procedure TFrmMain.FormCreate(Sender: TObject);
begin
  ExtendedData := nil;
end;

function TFrmMain.LoadCharData(AFileName: String; out ADataout: TArray3DExt;
  var Dims: array of word): Integer;
var
  DataFile: File of Byte;
  i, j, k: Integer;
  DataPoint: Byte;
  Counter: Integer;
begin
  AssignFile(DataFile, AFileName);
  Reset(DataFile);

  SetLength(ADataout, Dims[0], Dims[1], Dims[2]);

  i := 0;
  j := 0;
  k := 0;
  Counter := 0;
  try
    repeat
      Read(DataFile, DataPoint);
      ADataout[i, j, k] := DataPoint;
      inc(i);
      if (i = Dims[0]) then
      begin
        i := 0;
        inc(j);
      end;
      if (j = Dims[1]) then
      begin
        j := 0;
        inc(k);
      end;
      inc(Counter);
    until Eof(DataFile);
  finally
    Closefile(DataFile);
  end;
  Result := Counter;
end;

procedure TFrmMain.miFileOpenClick(Sender: TObject);
var
  DataAmount: cardinal;
begin
  Dimensions['x'] := StrToInt(LEXDim.Text);
  Dimensions['y'] := StrToInt(LEYDim.Text);
  Dimensions['z'] := StrToInt(LEZDim.Text);
  OpenDialog.InitialDir := ExtractFilePath(ParamStr(0));
  OpenDialog.Filter := 'Volumes|*.vol';
  if OpenDialog.Execute() then
  begin
    DataAmount := LoadCharData(OpenDialog.FileName, ExtendedData, Dimensions);
    MessageDlg(format('%d read. %dx%dx%d', [DataAmount, Dimensions['x'],
      Dimensions['y'], Dimensions['z']]), mtInformation, [mbOK], -1);
    LEXDim.Text := Format('%d', [Dimensions['x']]);
    LEYDim.Text := Format('%d', [Dimensions['y']]);
    LEZDim.Text := Format('%d', [Dimensions['z']]);
  end;
  rbgAlgorithmClick(Self);
  rbgSurfaceClick(Self);
end;

procedure TFrmMain.rbgAlgorithmClick(Sender: TObject);
var
  IsoSurfaceEx: TGLIsoSurfaceExtractor;
  i: Integer;
  mo: TGLMeshObject;

begin
  // Create IsoSurfaceExtractor
  IsoSurfaceEx := TGLIsoSurfaceExtractor.Create(Dimensions['x'], Dimensions['y'],
    Dimensions['z'], ExtendedData);
  // Launch Calculation
  case rbgAlgorithm.ItemIndex of
    0:
      IsoSurfaceEx.MarchingTetrahedra(StrToFloat(LEIsoVal.Text), ExtractedVertices,
        ExtractedTriangles, False);
    1:
      IsoSurfaceEx.MarchingCubes(StrToFloat(LEIsoVal.Text), ExtractedVertices,
        ExtractedTriangles, False);
  end;

  lblVertices.Caption := Format('%d', [Length(ExtractedVertices)]);
  lblTriangles.Caption := Format('%d', [Length(ExtractedTriangles) div 3]);
  IsoSurfaceEx.Free();

  ffCube.MeshObjects.Clear();
  mo := TGLMeshObject.CreateOwned(ffCube.MeshObjects);
  for i := 0 to Length(ExtractedTriangles) - 1 do
    mo.Vertices.Add(AffineVectorMake(ExtractedVertices[ExtractedTriangles[i]].X
      - Dimensions['x'] / 2, ExtractedVertices[ExtractedTriangles[i]].Y -
      Dimensions['y'] / 2, ExtractedVertices[ExtractedTriangles[i]].Z -
      Dimensions['z'] / 2));
  ffCube.StructureChanged();

  GLSceneViewer.Invalidate();
end;

procedure TFrmMain.rbgSurfaceClick(Sender: TObject);
begin
  if rbgSurface.ItemIndex = 0 then
    ffCube.Material.PolygonMode := pmFill
  else
    ffCube.Material.PolygonMode := pmLines;
  GLSceneViewer.Invalidate();
end;

procedure TFrmMain.miFileExitClick(Sender: TObject);
begin
  SetLength(ExtendedData, 0, 0, 0);
  Application.Terminate();
end;

procedure TFrmMain.FormMouseWheel(Sender: TObject; Shift: TShiftState;
  WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
begin
  Camera.AdjustDistanceToTarget(Power(1.1, WheelDelta / 120));
end;

procedure TFrmMain.GLSceneViewerMouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  mdx := X;
  mdy := Y;
end;

procedure TFrmMain.GLSceneViewerMouseMove(Sender: TObject; Shift: TShiftState;
  X, Y: Integer);

var
  dx, dy: Integer;
  v: TGLVector;
begin
  // calculate delta since last move or last mousedown
  dx := mdx - X;
  dy := mdy - Y;
  mdx := X;
  mdy := Y;
  if ssLeft in Shift then
  begin
    if ssShift in Shift then
    begin
      // right button with shift rotates the teapot
      // (rotation happens around camera's axis)
      Camera.RotateObject(dcBox, dy, dx);
    end
    else
    begin
      // right button without shift changes camera angle
      // (we're moving around the parent and target dummycube)
      Camera.MoveAroundTarget(dy, dx)
    end;
  end
  else if Shift = [ssRight] then
  begin
    // left button moves our target and parent dummycube
    v := Camera.ScreenDeltaToVectorXY(dx, -dy,
      0.12 * Camera.DistanceToTarget / Camera.FocalLength);
    dcBox.Position.Translate(v);
    // notify camera that its position/target has been changed
    Camera.TransformationChanged;
  end;
end;

end.
